package top.bbman.boot.redis.bean;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import top.bbman.boot.logger.LogConstant;
import top.bbman.boot.redis.constant.PeaRedisBeanName;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * list operations
 *
 * @author 祥霸
 * @version 1.0
 */
@Component(PeaRedisBeanName.DIY_LIST_REDIS)
public class PeaListRedis<E>{

    @PostConstruct
    private void init() {
        LogConstant.bbManRedis.info("init list redis");
    }

    @Resource
    private RedisTemplate<String, E> redisTemplate;

    /**
     * Returns the number of elements in this list
     * @param key redis key
     * @return the number of elements in this list
     */
    public Long size(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * Returns <tt>true</tt> if this list contains no elements
     *
     * @param key redis key
     * @return <tt>true</tt> if this list contains no elements
     */
    public boolean isEmpty(String key) {
        return this.size(key) == 0L;
    }

    /**
     * Returns <tt>true</tt> if this list contains the specified element
     *
     * @param key redis key
     * @param value element whose presence in this list is to be tested
     * @return  <tt>true</tt> if this list contains the specified element
     */
    public boolean contains(String key, E value) {
        List<E> list = this.getAll(key);
        return list.contains(value);
    }

    /**
     * Returns an array containing all of the elements in this list in proper sequence (from first to last element).
     *
     * @param key redis key
     * @return an array containing all of the elements in this list in proper sequence
     */
    public Object[] toArray(String key) {
        List<E> list = this.getAll(key);
        return list.toArray();
    }

    /**
     * Appends the specified element to the end of this list
     *
     * @param key redis key
     * @param value element to be appended to this list
     * @return <tt>true</tt>
     */
    public boolean add(String key, E value) {
        Long index = redisTemplate.boundListOps(key).rightPush(value);
        return Objects.nonNull(index) && index > 0L;
    }

    /**
     * Removes the first occurrence of the specified element from this list
     * @param key key
     * @param value e
     * @return <tt>true</tt>
     */
    public boolean remove(String key, E value) {
        Long index = redisTemplate.opsForList().remove(key, 1, value);
        return Objects.nonNull(index) && index > 0L;
    }

    /**
     * Returns <tt>true</tt> if this list contains all of the elements of the
     * specified collection.
     *
     * @param key redis key
     * @param c collection to be checked for containment in this list
     * @return <tt>true</tt> if this list contains all of the elements of the
     *         specified collection
     */
    public boolean containsAll(String key, Collection<E> c) {
        List<E> list = this.getAll(key);
        return list.containsAll(c);
    }

    /**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the specified
     * collection's iterator (optional operation)
     *
     * @param key redis key
     * @param c elements
     * @return <tt>true</tt>
     */
    public boolean addAll(String key, Collection<E> c) {
        redisTemplate.opsForList().rightPushAll(key, c);
        return true;
    }

    /**
     * Returns the element at the specified position in this list.
     *
     * @param key redis key
     * @param index index of the element to return
     * @return the element at the specified position in this list
     */
    public E get(String key, int index){
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * Replaces the element at the specified position in this list with the
     * specified element (optional operation).
     *
     * @param key redis key
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     */
    public E set(String key, int index, E element){
        List<String> keys = new ArrayList<>();
        keys.add(key);

        DefaultRedisScript<E> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType((Class<E>) element.getClass());
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/list/set.lua")));
        return redisTemplate.execute(redisScript, keys, index, element);
    }

    /**
     * Returns a view of the portion of this list between the specified
     *
     * @param key redis key
     * @param fromIndex low endpoint (inclusive) of the subList
     * @param toIndex high endpoint (exclusive) of the subList
     * @return a view of the specified range within this list
     */
    public List<E> subList(String key, int fromIndex, int toIndex) {
        List<E> list = redisTemplate.opsForList().range(key, 0, -1);
        assert list != null;
        return list.subList(fromIndex, toIndex);
    }

    /**
     * Returns the element at the all in this list.
     *
     * @param key redis key
     * @return all element
     */
    public List<E> getAll(String key) {
        BoundListOperations<String, E> listOperations = redisTemplate.boundListOps(key);
        return listOperations.range(0, -1);
    }


}
